//
//  UIView+HUD.swift
//  StarTimesTV
//
//  Created by zhaozh on 2016/11/2.
//  Copyright © 2016年 Startimes. All rights reserved.
//

import UIKit

public class HUDStyle2 {
    
    public static var `default`: HUDStyle2 = HUDStyle2()
    
    var color: UIColor = UIColor.red
    var backViewColor: UIColor = UIColor.clear
    var enabled: Bool = true
    
    private var container: UIView
    
    init(container: UIView) {
        self.container = container
       
        color = HUDStyle2.default.color
        backViewColor = HUDStyle2.default.backViewColor
        enabled = HUDStyle2.default.enabled
    }
    
    private init() {
        self.container = UIView()
    }
   
    public func color(_ color: UIColor) -> HUDStyle2 {
        self.color = color
        return self
    }
    
    public func backViewColor(_ backViewColor: UIColor) -> HUDStyle2 {
        self.backViewColor = backViewColor
        return self
    }
    
    public func enabled(_ enabled: Bool) -> HUDStyle2 {
        self.enabled = enabled
        return self
    }
    
    public func show2() {
        var hud: STSHUD2
        var isExisted: Bool = false
        if let view = container.HUD2() {
            isExisted = true
            hud = view
        } else {
            hud = STSHUD2(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
        }
        
        hud.indicator?.strokeColor = color
        hud.descBackView.backgroundColor = backViewColor
        
        container.isUserInteractionEnabled = enabled
        
        if isExisted {
            container.bringSubviewToFront(hud)
        } else {
            container.addSubview(hud)
        }
        hud.resetIndicatorAnimating()
    }
    
    public func hide() {
        DispatchQueue.main.HUDSafeAsync {
            self.container.isUserInteractionEnabled = true
            self.container.HUD2()?.removeFromSuperview()
        }
    }
}

extension UIView {
    
    public var hud2: HUDStyle2 {
        return HUDStyle2(container: self)
    }
    
    func HUD2() -> STSHUD2? {
        let reverse = self.subviews.reversed()
        for subview in reverse {
            if subview.isKind(of: STSHUD2.self) {
                return subview as? STSHUD2
            }
        }
        return nil
    }
}

class STSHUD2: UIView {
    lazy var contentView: UIView = {
        let view = UIView()
        view.translatesAutoresizingMaskIntoConstraints = false
        view.backgroundColor = UIColor.clear
        view.isUserInteractionEnabled = true
        return view
    }()
    lazy var indicator: SARMaterialDesignSpinner2? = {
        let view = SARMaterialDesignSpinner2(frame: CGRect(x: 0, y: 0, width: 36, height: 36))
        view.translatesAutoresizingMaskIntoConstraints = false
        view.lineWidth = 4
        view.strokeColor = UIColor(hex: 0xFFFFFF, alpha: 0.9)
        
        return view
    }()
    
    private var _descBackView: UIView?
    var descBackView: UIView {
        get {
            if let view = _descBackView {
                return view
            }
            let view = UIView()
            view.translatesAutoresizingMaskIntoConstraints = false
            view.backgroundColor = UIColor.clear
            _descBackView = view
            return view
        }
    }
    
    var shouldShowDescBackView: Bool {
        if let view = _descBackView {
            return view.backgroundColor != UIColor.clear
        }
        return false
    }
    
    var tapAction: (() -> Void)?
    
    //MARK: super methods
    override init(frame: CGRect) {
        super.init(frame: frame)
        self.backgroundColor = UIColor.clear
        self.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    }
    
    func resetIndicatorAnimating(){
        self.indicator?.stopAnimating()
        self.indicator?.startAnimating()
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func willMove(toSuperview newSuperview: UIView?) {
        DispatchQueue.main.HUDSafeAsync {
            super.willMove(toSuperview: newSuperview)
            if newSuperview == nil {
                self.superview?.removeObserver(self, forKeyPath: "frame")
            }
        }
    }

    override func didMoveToSuperview() {
        super.didMoveToSuperview()
        if (superview != nil) {
            self.frame = superview!.bounds
            superview?.addObserver(self, forKeyPath: "frame", options: [.new, .initial, .old], context: nil)

            setUpConstraints()
            indicator?.startAnimating()
        }
    }
    
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if keyPath == "frame" {
            if let rect = change?[NSKeyValueChangeKey.newKey] as? CGRect {
                self.frame = CGRect(x: 0, y: 0, width: rect.width, height: rect.height)
                indicator?.stopAnimating()
                indicator?.startAnimating()
            }
        }
    }
    
    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        let hitTestView = super.hitTest(point, with: event)
        if let view = hitTestView, view.isKind(of: UIControl.self) {
            return hitTestView
        }
        if let view = hitTestView, view == contentView {
            return view
        }
        return nil
    }
    
    // MARK: actions
    func buttonAction() {
        if (tapAction != nil) {
            tapAction!()
        }
    }
    
    // MARK: constraints
    func setUpConstraints() {
        contentView.subviews.forEach { (view) in
            view.removeFromSuperview()
        }
        contentView.removeFromSuperview()
        
        removeConstraints(self.constraints)
        contentView.removeConstraints(contentView.constraints)
        
        self.addSubview(contentView)
        if shouldShowDescBackView {
            contentView.addSubview(_descBackView!)
        }
        
        contentView.addSubview(indicator!)
        
        addConstraint(equalTo(view: contentView, attribute: .centerX))
        addConstraint(equalTo(view: contentView, attribute: .centerY, constant: 0))
        
        let padding = 20
        let verticalSpace = 16.0
        
        var subviewKeys: [String] = []
        var subviewKeyObjects: [String : Any] = [:]
        let metrics: [String : Any] = ["padding": padding]
        
        subviewKeys.append("indicator")
        subviewKeyObjects[subviewKeys.last!] = indicator
        contentView.addConstraint(contentView.equalTo(view: indicator!, attribute: .centerY))
        indicator?.addConstraint(indicator!.equalTo(attribute: .width, constant: 36))
        indicator?.addConstraint(indicator!.equalTo(attribute: .height, constant: 36))
        
        // horizontal fromat
        var horizontalFormat: String = ""
        for i in 0..<subviewKeys.count {
            horizontalFormat.append("[" + subviewKeys[i] + "]")
            if i < subviewKeys.count - 1 {
                horizontalFormat.append(String(format: "-(%.f@750)-", verticalSpace))
            }
        }
        if horizontalFormat.isEmpty == false {
            contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-" + horizontalFormat + "-|", options: .alignAllCenterY, metrics: metrics, views: subviewKeyObjects))
        }
    }
}

extension DispatchQueue {
    func HUDSafeAsync(_ block: @escaping ()->()) {
        if self === DispatchQueue.main && Thread.isMainThread {
            block()
        } else {
            async { block() }
        }
    }
}

extension UIView {
    func equalTo(view: UIView, attribute: NSLayoutConstraint.Attribute, constant: CGFloat = 0.0) -> NSLayoutConstraint {
        return NSLayoutConstraint(item: view, attribute: attribute, relatedBy: .equal, toItem: self, attribute: attribute, multiplier: 1.0, constant: constant)
    }
    func equalTo(attribute: NSLayoutConstraint.Attribute, constant: CGFloat = 0.0) -> NSLayoutConstraint {
        return NSLayoutConstraint(item: self, attribute: attribute, relatedBy: .equal, toItem: nil, attribute: attribute, multiplier: 1.0, constant: constant)
    }
}

private let kSARRingStrokeAnimationKey2 = "sarmaterialdesignspinner.stroke"
private let kSARRingRotationAnimationKey2 = "sarmaterialdesignspinner.rotation"
private let kSARRingStrokeAnimationDuration2 = 1.5

class SARMaterialDesignSpinner2: UIView {
    let progressLayer = CAShapeLayer()
    var timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
    var isAnimating = false
  
    var count = 0
    let googleColorsArray = [UIColor.blue, UIColor.red, UIColor.yellow, UIColor.green]
    
    //    MARK: -Initializers
    override init (frame : CGRect) {
        super.init(frame : frame)
        setupProgressLayer()
    }
    
    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)!
        setupProgressLayer()
    }
    
    override func layoutSubviews() {
        self.progressLayer.frame = CGRect(x: 0, y: 0, width: self.bounds.width, height: self.bounds.height);
        self.updateProgressLayerPath()
    }
    
    //    MARK: -Setup Methods
    
    func setupProgressLayer() {
        progressLayer.strokeColor = UIColor.red.cgColor
        progressLayer.fillColor = nil
        progressLayer.lineWidth = 2.0
        self.layer.addSublayer(progressLayer)
        self.updateProgressLayerPath()
    }
    
    func updateProgressLayerPath() {
        let center = CGPoint(x: self.bounds.midX, y: self.bounds.midY)
        let radius = min(self.bounds.width / 2, self.bounds.height / 2) - self.progressLayer.lineWidth / 2
        let startAngle: CGFloat = 0
        let endAngle: CGFloat = 2*CGFloat.pi
        let path = UIBezierPath(arcCenter: center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: true)
        progressLayer.path = path.cgPath
        
        progressLayer.strokeStart = 0.0
        progressLayer.strokeEnd = 0.0
        
        progressLayer.backgroundColor = UIColor.random.cgColor
    }
    
    //    MARK: -Accessors
    var lineWidth: CGFloat {
        get {
            return progressLayer.lineWidth
        }
        set(newValue) {
            progressLayer.lineWidth = newValue
            updateProgressLayerPath()
        }
    }
    
    var strokeColor: UIColor {
        get {
            return UIColor(cgColor: self.progressLayer.strokeColor!)
        }
        set(newValue) {
            self.progressLayer.strokeColor = newValue.cgColor
        }
    }
    
    //    MARK: -Animation Methods
    func startAnimating() {
        if isAnimating { return }
        isAnimating = true
        
        let animation = CABasicAnimation(keyPath: "transform.rotation")
        animation.duration = 4
        animation.isRemovedOnCompletion = false
        animation.fromValue = 0
        animation.toValue = (2 * Float.pi)
        animation.repeatCount = Float.infinity
        
//        self.progressLayer.removeAnimation(forKey: kSARRingRotationAnimationKey2)
        self.progressLayer.add(animation, forKey: kSARRingRotationAnimationKey2)

        let headAnimation = CABasicAnimation(keyPath: "strokeStart")
        headAnimation.duration = 1
        headAnimation.fromValue = 0
        headAnimation.toValue = 0.25
        headAnimation.timingFunction = self.timingFunction

        let tailAnimation = CABasicAnimation(keyPath: "strokeEnd")
        tailAnimation.duration = 1
        tailAnimation.fromValue = 0
        tailAnimation.toValue = 1
        tailAnimation.timingFunction = self.timingFunction;

        let endHeadAnimation = CABasicAnimation(keyPath: "strokeStart")
        endHeadAnimation.beginTime = 1
        endHeadAnimation.duration = 0.5
        endHeadAnimation.fromValue = 0.25
        endHeadAnimation.toValue = 1
        endHeadAnimation.timingFunction = self.timingFunction

        let endTailAnimation = CABasicAnimation(keyPath: "strokeEnd")
        endTailAnimation.beginTime = 1
        endTailAnimation.duration = 0.5
        endTailAnimation.fromValue = 1
        endTailAnimation.toValue = 1
        endTailAnimation.timingFunction = self.timingFunction

        let animations = CAAnimationGroup()
        animations.isRemovedOnCompletion = false
        animations.duration = kSARRingStrokeAnimationDuration2
        animations.animations = [headAnimation, tailAnimation, endHeadAnimation, endTailAnimation]
        animations.repeatCount = Float.infinity;
        self.progressLayer.removeAnimation(forKey: kSARRingStrokeAnimationKey2)
        self.progressLayer.add(animations, forKey: kSARRingStrokeAnimationKey2)
    }
    
    func stopAnimating() {
        if !self.isAnimating { return }
        
        self.progressLayer.removeAnimation(forKey: kSARRingRotationAnimationKey2)
        self.progressLayer.removeAnimation(forKey: kSARRingStrokeAnimationKey2)
        self.isAnimating = false;
    }
}
